package com.djnt.catering;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.EmptyStackException;
import java.util.Hashtable;
import java.util.Stack;
import java.util.TreeSet;

import net.sourceforge.pinyin4j.PinyinHelper;
import android.util.Log;

public class QuickQueryStack {

	private static final String TAG = "QuickQueryStack";
	private static Hashtable<Character, String> mMapCharToString;
	Item[] mAllItems;
	private IQuery mQuery;

	public static class ItemComparator implements Comparator<Item> {

		public int compare(Item o1, Item o2) {
			return o1.pinyin.compareTo(o2.pinyin);
		}
	}

	static class PinyinChar extends ArrayList<String> {

		private PinyinChar() {
			super();
			// TODO Auto-generated constructor stub
		}

		public PinyinChar matcher(char c) {
			// TODO Auto-generated method stub
			PinyinChar b = new PinyinChar();
			for (String a1 : this) {
				if (a1.length() > 0 && a1.charAt(0) == c) {
					b.add(a1.substring(1));
				}
			}
			if (b.size() == 0) {
				return null;
			}
			return b;
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			for (String s : this) {
				sb.append(' ').append(s);
			}
			return sb.toString();
		}

	}

	static class PinyinWord extends ArrayList<PinyinChar> {

		private PinyinWord parent;

		private PinyinWord(int capacity) {
			super(capacity);
			// TODO Auto-generated constructor stub
		}

		public PinyinWord matcher(char c, int i) {
			// TODO Auto-generated method stub
			PinyinChar a = this.get(i).matcher(c);
			if (a == null) {
				return null;
			}
			PinyinWord t = new PinyinWord(i + 1);
			t.parent = this;
			t.add(a);
			for (i = i + 1; i < this.size(); i++) {
				t.add(this.get(i));
			}
			return t;
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			for (PinyinChar s : this) {
				sb.append(s).append('\t');
			}
			return sb.toString();
		}

		public int getItemMatchCount(PinyinWord root) {
			// TODO Auto-generated method stub
			if (root == null || root.size() == 0) {
				return 0;
			}
			int i = 0;
			int j = 0;
			for (i = this.size() - 1, j = root.size() - 1; i >= 0; i--, j--) {
				String s1 = this.get(i).toString();
				String s2 = root.get(j).toString();
				if (!s1.equals(s2)) {
					return j + 1;
				}
			}
			return j + 1;
		}

		public int getItemMatchData() {
			// TODO Auto-generated method stub
			PinyinWord first = getFirstItem();
			PinyinWord root = first.parent;
			if (root == null) {
				// no any char input
				return 0;
			}
			int end = getItemMatchCount(root);
			int start;
			if (this == first) {
				start = end;
			} else {
				start = first.getItemMatchCount(root);
			}
			if (start < 1 || end < 1 || end < start) {
				return 0;
			}
			return (start - 1) | (end << 16);
		}

		private PinyinWord getFirstItem() {
			// TODO Auto-generated method stub
			if (parent != null && parent.parent != null) {
				return parent.getFirstItem();
			}
			return this;
		}
	}

	static class PinyinLookup extends ArrayList<PinyinWord> {

		private PinyinLookup() {
			super();
			// TODO Auto-generated constructor stub
		}

		public PinyinLookup(String name, StringBuilder sb) {
			PinyinWord word = new PinyinWord(name.length());
			for (int i = 0; i < name.length(); i++) {
				PinyinChar j = new PinyinChar();
				char c = name.charAt(i);
				String[] py = PinyinHelper.toHanyuPinyinStringArray(c);
				if (py != null && py.length > 0) {
					TreeSet<String> b = new TreeSet<String>();
					for (String a : py) {
						String a1 = a.replaceAll("[0-9]", "");
						b.add(a1);
						sb.append(a);
					}
					b.add(String.valueOf(c));
					sb.append(c);
					for (String a : b) {
						j.add(a);
					}
				} else {
					j.add(String.valueOf(c));
					sb.append(c);
				}
				word.add(j);
			}
			add(word);
		}

		public PinyinLookup matcher(Item item, char c) {
			// TODO Auto-generated method stub
			PinyinLookup s = new PinyinLookup();
			for (PinyinWord a : this) {
				int size = a.size();
				if (size > 2) {
					if (a.parent != null) {
						size = 2;
					}
				}
				for (int i = 0; i < size; i++) {
					PinyinWord a1 = a.matcher(c, i);
					if (a1 != null) {
						s.add(a1);
					}
				}
			}
			if (s.size() != 0) {
				return s;
			}
			return null;
		}

	}

	class Item {
		private String pinyin;
		Object id;
		PinyinLookup[] pinyin_lookup;

		public Item clone() {
			// TODO Auto-generated method stub
			Item i = new Item();
			i.pinyin = pinyin;
			i.id = id;
			i.pinyin_lookup = pinyin_lookup;
			return i;
		}

		public Item(Object o) {
			// TODO Auto-generated constructor stub
			id = o;
			StringBuilder sb = new StringBuilder();
			String[] name = mQuery.getText(o);
			int size = name.length;
			pinyin_lookup = new PinyinLookup[size];
			for (int i = 0; i < size; i++) {
				pinyin_lookup[i] = new PinyinLookup(name[i], sb);
			}
			pinyin = sb.toString();
		}

		private Item() {

		}

		public Item matcher(char c) {
			int size = pinyin_lookup.length;
			PinyinLookup[] a = new PinyinLookup[size];
			boolean succ = false;
			for (int i = 0; i < size; i++) {
				if (pinyin_lookup[i] != null) {
					a[i] = pinyin_lookup[i].matcher(this, c);
					if (a[i] != null) {
						succ = true;
					}
				}
			}
			if (succ) {
				Item i = new Item();
				i.id = id;
				i.pinyin_lookup = a;
				return i;
			}
			return null;
		}

		@Override
		public String toString() {
			return "Item [id=" + id + ", pinyin_lookup=" + pinyin_lookup + "]";
		}

		public int[] getItemMatchData() {
			// TODO Auto-generated method stub
			int size = pinyin_lookup.length;
			int[] t = new int[size];
			for (int i = 0; i < size; i++) {
				t[i] = getItemMatchData(i);
			}
			return t;
		}

		private int getItemMatchData(int i) {
			// TODO Auto-generated method stub
			PinyinLookup a = pinyin_lookup[i];
			if (a == null || a.size() == 0) {
				return 0;
			}
			return a.get(0).getItemMatchData();
		}

	};

	static interface ItemQuery {

		public boolean match(Object id);

	};

	static {
		QuickQueryStack.mMapCharToString = new Hashtable<Character, String>();

		QuickQueryStack.mMapCharToString.put('1', "1");
		QuickQueryStack.mMapCharToString.put('2', "[2abc]");
		QuickQueryStack.mMapCharToString.put('3', "[3def]");
		QuickQueryStack.mMapCharToString.put('4', "[4ghi]");
		QuickQueryStack.mMapCharToString.put('5', "[5jkl]");
		QuickQueryStack.mMapCharToString.put('6', "[6mno]");
		QuickQueryStack.mMapCharToString.put('7', "[7pqrs]");
		QuickQueryStack.mMapCharToString.put('8', "[8tuv]");
		QuickQueryStack.mMapCharToString.put('9', "[9wxyz]");
		QuickQueryStack.mMapCharToString.put('0', "[0+]");
	}

	public QuickQueryStack(IQuery q, Collection<? extends Object> c) {
		mQuery = q;
		TreeSet<Item> a = new TreeSet<Item>(new ItemComparator());
		for (Object o : c) {
			a.add(new Item(o));
		}
		mAllItems = new Item[a.size()];
		a.toArray(mAllItems);
	}

	private QuickQueryStack() {
		// TODO Auto-generated constructor stub
	}

	public QuickQueryStack query(ItemQuery cmp) {
		QuickQueryStack q = new QuickQueryStack();
		q.mQuery = mQuery;
		ArrayList<Item> a = new ArrayList<Item>(mAllItems.length);
		for (Item o : mAllItems) {
			if (cmp.match(o.id)) {
				a.add(o.clone());
			}
		}
		q.mAllItems = new Item[a.size()];
		a.toArray(q.mAllItems);
		return q;
	}

	Object getItemEntry(int position) {
		return getItem(position).id;
	}

	private Item[] getInternalItems() {
		if (mStackCandidateStates.empty()) {
			return mAllItems;
		}
		CandidateState c = mStackCandidateStates.peek();
		return c.mItems;
	}

	private Item getItem(int position) {
		Item[] s = getInternalItems();
		if (position >= 0 && position < s.length) {
			return s[position];
		}
		return null;
	}

	int getCount() {
		return getInternalItems().length;
	}

	public CandidateState GetCandidateState() {
		if (mStackCandidateStates != null && !mStackCandidateStates.empty()) {
			CandidateState state = null;
			try {
				state = mStackCandidateStates.peek();
			} catch (EmptyStackException e) {
				Log.v(TAG, "GetCandidateState ::" + e);
				state = null;
			}
			return state;
		} else {
			return null;
		}
	}

	public class CandidateState {
		private char mc_input;
		private Item[] mItems = new Item[0];

		public CandidateState(char c) {
			this.mc_input = c;
			Item[] items = mAllItems;
			if (!mStackCandidateStates.empty()) {
				CandidateState cs = mStackCandidateStates.peek();
				items = cs.mItems;
				if (items.length == 0) {
					return;
				}
			}

			ArrayList<Item> s = new ArrayList<Item>(items.length);
			for (Item it : items) {
				Item m = it.matcher(c);
				if (m != null) {
					s.add(m);
				}
			}
			mItems = new Item[s.size()];
			s.toArray(mItems);
		}
	}

	public CandidateState onTextChanged(String strInput) {

		if (strInput == null || strInput.length() == 0) {
			mStackCandidateStates.clear();
			return null;
		}
		Object[] array = mStackCandidateStates.toArray();
		int i = 0;
		int j = 0;

		int arrLen = array.length;
		int inputLen = strInput.length();

		for (i = 0, j = 0; i < arrLen && j < inputLen; i++, j++) {
			CandidateState c = (CandidateState) array[i];
			if (c.mc_input != strInput.charAt(j)) {
				break;
			}
		}

		while (i++ < arrLen) {
			mStackCandidateStates.pop();
		}

		for (; j < inputLen; j++) {
			mStackCandidateStates.push(new CandidateState(strInput.charAt(j)));
		}

		if (!mStackCandidateStates.empty()) {
			return mStackCandidateStates.peek();
		}
		return null;
	}

	private Stack<CandidateState> mStackCandidateStates = new Stack<CandidateState>();

	public static interface IQuery {
		public String[] getText(Object entry);
	}

	static char toHexCharacter(int c) {
		if (c < 10) {
			return (char) (c + '0');
		}
		return (char) (c - 10 + 'a');
	}

	String escapeString(char c) {
		String s = mMapCharToString.get(c);
		if (s != null) {
			return s;
		}
		if (Character.isLetterOrDigit(c)) {
			return String.valueOf(c);
		}
		if (c >= 0x21 && c < 0x7F) {
			int lo = c & 0x0f;
			int hi = (c >> 8) & 0x0f;
			return "\\x" + toHexCharacter(hi)
					+ String.valueOf(toHexCharacter(lo));
		}
		return String.valueOf(c);
	}

	public int[] getItemMatchData(int position) {
		// TODO Auto-generated method stub
		Item item = getItem(position);
		if (item != null) {
			return item.getItemMatchData();
		}
		return null;
	}
}
